#include <utils/base64/zbase64.h>

static const char fillchar = '=';
static const string::size_type np = string::npos;

const string ZBase64::Base64Table("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");

const string ZBase64::Base64StdTable("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");


// Decode Table gives the index of any valid base64 character in the Base64 table]
// 65 == A, 97 == a, 48 == 0, 43 == +, 47 == /

// 0  1  2  3  4  5  6  7  8  9
const string::size_type ZBase64::DecodeTable[] = {np, np, np, np, np, np, np, np, np, np,  // 0 - 9
                                                  np, np, np, np, np, np, np, np, np, np,  //10 -19
                                                  np, np, np, np, np, np, np, np, np, np,  //20 -29
                                                  np, np, np, np, np, np, np, np, np, np,  //30 -39
                                                  np, np, np, np, np, 62, np, np, 52, 53,  //40 -49 -_
                                                  54, 55, 56, 57, 58, 59, 60, 61, np, np,  //50 -59
                                                  np, np, np, np, np, 0, 1, 2, 3, 4,  //60 -69
                                                  5, 6, 7, 8, 9, 10, 11, 12, 13, 14,  //70 -79
                                                  15, 16, 17, 18, 19, 20, 21, 22, 23, 24,  //80 -89
                                                  25, np, np, np, np, 63, np, 26, 27, 28,  //90 -99 -_
                                                  29, 30, 31, 32, 33, 34, 35, 36, 37, 38,  //100 -109
                                                  39, 40, 41, 42, 43, 44, 45, 46, 47, 48,  //110 -119
                                                  49, 50, 51, np, np, np, np, np, np, np,  //120 -129
                                                  np, np, np, np, np, np, np, np, np, np,  //130 -139
                                                  np, np, np, np, np, np, np, np, np, np,  //140 -149
                                                  np, np, np, np, np, np, np, np, np, np,  //150 -159
                                                  np, np, np, np, np, np, np, np, np, np,  //160 -169
                                                  np, np, np, np, np, np, np, np, np, np,  //170 -179
                                                  np, np, np, np, np, np, np, np, np, np,  //180 -189
                                                  np, np, np, np, np, np, np, np, np, np,  //190 -199
                                                  np, np, np, np, np, np, np, np, np, np,  //200 -209
                                                  np, np, np, np, np, np, np, np, np, np,  //210 -219
                                                  np, np, np, np, np, np, np, np, np, np,  //220 -229
                                                  np, np, np, np, np, np, np, np, np, np,  //230 -239
                                                  np, np, np, np, np, np, np, np, np, np,  //240 -249
                                                  np, np, np, np, np, np               //250 -256
};

const string::size_type ZBase64::DecodeStdTable[] = {np, np, np, np, np, np, np, np, np, np,  // 0 - 9
                                                     np, np, np, np, np, np, np, np, np, np,  //10 -19
                                                     np, np, np, np, np, np, np, np, np, np,  //20 -29
                                                     np, np, np, np, np, np, np, np, np, np,  //30 -39
                                                     np, np, np, 62, np, np, np, 63, 52, 53,  //40 -49 +/
                                                     54, 55, 56, 57, 58, 59, 60, 61, np, np,  //50 -59
                                                     np, np, np, np, np, 0, 1, 2, 3, 4,  //60 -69
                                                     5, 6, 7, 8, 9, 10, 11, 12, 13, 14,  //70 -79
                                                     15, 16, 17, 18, 19, 20, 21, 22, 23, 24,  //80 -89
                                                     25, np, np, np, np, np, np, 26, 27, 28,  //90 -99 +/
                                                     29, 30, 31, 32, 33, 34, 35, 36, 37, 38,  //100 -109
                                                     39, 40, 41, 42, 43, 44, 45, 46, 47, 48,  //110 -119
                                                     49, 50, 51, np, np, np, np, np, np, np,  //120 -129
                                                     np, np, np, np, np, np, np, np, np, np,  //130 -139
                                                     np, np, np, np, np, np, np, np, np, np,  //140 -149
                                                     np, np, np, np, np, np, np, np, np, np,  //150 -159
                                                     np, np, np, np, np, np, np, np, np, np,  //160 -169
                                                     np, np, np, np, np, np, np, np, np, np,  //170 -179
                                                     np, np, np, np, np, np, np, np, np, np,  //180 -189
                                                     np, np, np, np, np, np, np, np, np, np,  //190 -199
                                                     np, np, np, np, np, np, np, np, np, np,  //200 -209
                                                     np, np, np, np, np, np, np, np, np, np,  //210 -219
                                                     np, np, np, np, np, np, np, np, np, np,  //220 -229
                                                     np, np, np, np, np, np, np, np, np, np,  //230 -239
                                                     np, np, np, np, np, np, np, np, np, np,  //240 -249
                                                     np, np, np, np, np, np               //250 -256
};


string ZBase64::encodeFromArray(const char* data, size_t len) {
    size_t i;
    int c;
    string ret;

    ret.reserve(len * 2);

    for (i = 0; i < len; ++i) {
        c = (data[i] >> 2) & 0x3f;
        ret.append(1, Base64Table[c]);
        c = (data[i] << 4) & 0x3f;
        if (++i < len)
            c |= (data[i] >> 4) & 0x0f;

        ret.append(1, Base64Table[c]);
        if (i < len) {
            c = (data[i] << 2) & 0x3f;
            if (++i < len)
                c |= (data[i] >> 6) & 0x03;

            ret.append(1, Base64Table[c]);
        } else {
            ++i;
            ret.append(1, fillchar);
        }

        if (i < len) {
            c = data[i] & 0x3f;
            ret.append(1, Base64Table[c]);
        } else {
            ret.append(1, fillchar);
        }
    }

    return (ret);
}

string ZBase64::encodeStdFromArray(const char* data, size_t len) {
    size_t i;
    int c;
    string ret;

    ret.reserve(len * 2);

    for (i = 0; i < len; ++i) {
        c = (data[i] >> 2) & 0x3f;
        ret.append(1, Base64StdTable[c]);
        c = (data[i] << 4) & 0x3f;
        if (++i < len)
            c |= (data[i] >> 4) & 0x0f;

        ret.append(1, Base64StdTable[c]);
        if (i < len) {
            c = (data[i] << 2) & 0x3f;
            if (++i < len)
                c |= (data[i] >> 6) & 0x03;

            ret.append(1, Base64StdTable[c]);
        } else {
            ++i;
            ret.append(1, fillchar);
        }

        if (i < len) {
            c = data[i] & 0x3f;
            ret.append(1, Base64StdTable[c]);
        } else {
            ret.append(1, fillchar);
        }
    }

    return (ret);
}

string ZBase64::encode(const string& data) {
    string::size_type i;
    int c;
    string::size_type len = data.length();
    string ret;

    ret.reserve(len * 2);

    for (i = 0; i < len; ++i) {
        c = (data[i] >> 2) & 0x3f;
        ret.append(1, Base64Table[c]);
        c = (data[i] << 4) & 0x3f;
        if (++i < len)
            c |= (data[i] >> 4) & 0x0f;

        ret.append(1, Base64Table[c]);
        if (i < len) {
            c = (data[i] << 2) & 0x3f;
            if (++i < len)
                c |= (data[i] >> 6) & 0x03;

            ret.append(1, Base64Table[c]);
        } else {
            ++i;
            ret.append(1, fillchar);
        }

        if (i < len) {
            c = data[i] & 0x3f;
            ret.append(1, Base64Table[c]);
        } else {
            ret.append(1, fillchar);
        }
    }

    return (ret);
}

string ZBase64::encodeStd(const string& data) {
    string::size_type i;
    int c;
    string::size_type len = data.length();
    string ret;

    ret.reserve(len * 2);

    for (i = 0; i < len; ++i) {
        c = (data[i] >> 2) & 0x3f;
        ret.append(1, Base64StdTable[c]);
        c = (data[i] << 4) & 0x3f;
        if (++i < len)
            c |= (data[i] >> 4) & 0x0f;

        ret.append(1, Base64StdTable[c]);
        if (i < len) {
            c = (data[i] << 2) & 0x3f;
            if (++i < len)
                c |= (data[i] >> 6) & 0x03;

            ret.append(1, Base64StdTable[c]);
        } else {
            ++i;
            ret.append(1, fillchar);
        }

        if (i < len) {
            c = data[i] & 0x3f;
            ret.append(1, Base64StdTable[c]);
        } else {
            ret.append(1, fillchar);
        }
    }

    return (ret);
}

string ZBase64::decode(const string& data) {
    string::size_type i;
    char c;
    char c1;
    string::size_type len = data.length();
    string ret;

    ret.reserve(len);

    for (i = 0; i < len; ++i) {
        c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
        ++i;
        c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
        c = (char) ((c << 2) | ((c1 >> 4) & 0x3));
        ret.append(1, c);
        if (++i < len) {
            c = data[i];
            if (fillchar == c)
                break;

            c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
            c1 = (char) (((c1 << 4) & 0xf0) | ((c >> 2) & 0xf));
            ret.append(1, c1);
        }

        if (++i < len) {
            c1 = data[i];
            if (fillchar == c1)
                break;

            c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
            c = (char) (((c << 6) & 0xc0) | c1);
            ret.append(1, c);
        }
    }

    return (ret);
}

string ZBase64::decodeStd(const string& data) {
    string::size_type i;
    char c;
    char c1;
    string::size_type len = data.length();
    string ret;

    ret.reserve(len);

    for (i = 0; i < len; ++i) {
        c = static_cast<char>(DecodeStdTable[static_cast<unsigned char>(data[i])]);
        ++i;
        c1 = static_cast<char>(DecodeStdTable[static_cast<unsigned char>(data[i])]);
        c = (char) ((c << 2) | ((c1 >> 4) & 0x3));
        ret.append(1, c);
        if (++i < len) {
            c = data[i];
            if (fillchar == c)
                break;

            c = static_cast<char>(DecodeStdTable[static_cast<unsigned char>(data[i])]);
            c1 = (char) (((c1 << 4) & 0xf0) | ((c >> 2) & 0xf));
            ret.append(1, c1);
        }

        if (++i < len) {
            c1 = data[i];
            if (fillchar == c1)
                break;

            c1 = static_cast<char>(DecodeStdTable[static_cast<unsigned char>(data[i])]);
            c = (char) (((c << 6) & 0xc0) | c1);
            ret.append(1, c);
        }
    }

    return (ret);
}
